//
// Created by user on 20-4-24.
//

#include "Slab.h"
#include "Malloc.h"

// 分配器缓冲数组
SlabCache kmallocCacheSize[16] =
        {
                {32,      0, 0, NULL, NULL, NULL, NULL},
                {64,      0, 0, NULL, NULL, NULL, NULL},
                {128,     0, 0, NULL, NULL, NULL, NULL},
                {256,     0, 0, NULL, NULL, NULL, NULL},
                {512,     0, 0, NULL, NULL, NULL, NULL},
                {1024,    0, 0, NULL, NULL, NULL, NULL},            //1KB
                {2048,    0, 0, NULL, NULL, NULL, NULL},
                {4096,    0, 0, NULL, NULL, NULL, NULL},            //4KB
                {8192,    0, 0, NULL, NULL, NULL, NULL},
                {16384,   0, 0, NULL, NULL, NULL, NULL},
                {32768,   0, 0, NULL, NULL, NULL, NULL},
                {65536,   0, 0, NULL, NULL, NULL, NULL},            //64KB
                {131072,  0, 0, NULL, NULL, NULL, NULL},            //128KB
                {262144,  0, 0, NULL, NULL, NULL, NULL},
                {524288,  0, 0, NULL, NULL, NULL, NULL},
                {1048576, 0, 0, NULL, NULL, NULL, NULL},            //1MB
        };

unsigned long slabInit() {

    Page *pPage = NULL;
    unsigned long *virtualAddress = NULL;
    int i, j;

    // 1. 获取到内存管理结构的结束地址
    unsigned long tmpAddress = memoryManagementStruct.endOfStruct;

//    printfColor(GREEN, BLACK, "1 test!!!\n");
    // 2. 初始化分配器数组
    for (i = 0; i < 16; i++) {

        // 1> 将内存管理结构的结束地址传给 pCachePool
        kmallocCacheSize[i].pCachePool = (Slab *) memoryManagementStruct.endOfStruct;

//        printfColor(GREEN, BLACK, "test!!!\n");
        // 2> 更新内存管理结构的结束地址，多加点是为了防止访问越界现象
        memoryManagementStruct.endOfStruct = memoryManagementStruct.endOfStruct + sizeof(Slab) + sizeof(long) * 10;

//        printfColor(GREEN, BLACK, "test!!!\n");
        // 3> 初始化 pCachePool 的 list 成员
        listInit(&kmallocCacheSize[i].pCachePool->list);

//        printfColor(GREEN, BLACK, "test!!!1\n");
        // 4> 初始化内存使用数量和释放数量
        kmallocCacheSize[i].pCachePool->usingCount = 0;
        kmallocCacheSize[i].pCachePool->freeCount = PAGE_2M_SIZE / kmallocCacheSize[i].size;

        // 5> 设置着色长度属性
        kmallocCacheSize[i].pCachePool->colorLength =
                ((PAGE_2M_SIZE / kmallocCacheSize[i].size + sizeof(unsigned long) * 8 - 1) >> 6) << 3;

        // 6> 设置着色数量
        kmallocCacheSize[i].pCachePool->colorCount = kmallocCacheSize[i].pCachePool->freeCount;

        // 7> 为着色映射位图分配内存
        kmallocCacheSize[i].pCachePool->pColorMap = (unsigned long *) memoryManagementStruct.endOfStruct;

        // 8> 更新内存管理结构的结束地址
        memoryManagementStruct.endOfStruct =
                (memoryManagementStruct.endOfStruct + kmallocCacheSize[i].pCachePool->colorLength +
                 sizeof(long) * 10) & (~(sizeof(long) - 1));

        // 9> 清空着色映射位图的内存
        memset(kmallocCacheSize[i].pCachePool->pColorMap, 0xff, kmallocCacheSize[i].pCachePool->colorLength);

        // 10> 初始化着色映射位图
        for (j = 0; j < kmallocCacheSize[i].pCachePool->colorCount; j++) {
            *(kmallocCacheSize[i].pCachePool->pColorMap + (j >> 6)) ^= 1UL << j % 64;
        }

        // 11> 初始化分配器使用总数记录
        kmallocCacheSize[i].totalFree = kmallocCacheSize[i].pCachePool->colorCount;
        kmallocCacheSize[i].totalUsing = 0;

    }

    // 3. 获取到当前内存管理结构的结束地址的 2M 页面的索引值
    i = (int) (Virtual_To_Physical(memoryManagementStruct.endOfStruct) >> PAGE_2M_SHIFT);

    // 4. 将刚才占用的物理的属性修改为内核使用的，防止物理页被其他程序使用
    for (j = (int) (PAGE_2M_ALIGN(Virtual_To_Physical(tmpAddress)) >> PAGE_2M_SHIFT); j <= i; j++) {

        pPage = memoryManagementStruct.pPageArray + j;
        *(memoryManagementStruct.pBitsMap + ((pPage->physicalAddress >> PAGE_2M_SHIFT) >> 6)) |=
                1UL << (pPage->physicalAddress >> PAGE_2M_SHIFT) % 64;
        pPage->pZone->pageUsingCount++;
        pPage->pZone->pageFreeCount--;
        pageInit(pPage, PG_PTable_Maped | PG_Kernel_Init | PG_Kernel);

    }

//    printfColor(ORANGE, BLACK,
//                "2.memoryManagementStruct.bits_map: %lx\tzone_struct->page_using_count: %d\tzone_struct->page_free_count: %d\n",
//                *memoryManagementStruct.pBitsMap, memoryManagementStruct.pZoneArray->pageUsingCount,
//                memoryManagementStruct.pZoneArray->pageFreeCount);

    // 5. 将每个分配器要使用的物理内存直接分配给他们，这里直接分配物理页是因为需要分配的内存连续性
    for (i = 0; i < 16; i++) {

        // 1. 以
        virtualAddress = (unsigned long *) ((memoryManagementStruct.endOfStruct + PAGE_2M_SIZE * i + PAGE_2M_SIZE - 1) &
                                     PAGE_2M_MASK_CODE);
        pPage = Virtual_To_2M_Page(virtualAddress);

        *(memoryManagementStruct.pBitsMap + ((pPage->physicalAddress >> PAGE_2M_SHIFT) >> 6)) |=
                1UL << (pPage->physicalAddress >> PAGE_2M_SHIFT) % 64;
        pPage->pZone->pageUsingCount++;
        pPage->pZone->pageFreeCount--;

        pageInit(pPage, PG_PTable_Maped | PG_Kernel_Init | PG_Kernel);

        kmallocCacheSize[i].pCachePool->pPage = pPage;
        kmallocCacheSize[i].pCachePool->pVirtualAddress = virtualAddress;

    }

//    printfColor(ORANGE, BLACK,
//                "3.memoryManagementStruct.bits_map: %lx\tzone_struct->page_using_count: %d\tzone_struct->page_free_count: %d\n",
//                *memoryManagementStruct.pBitsMap, memoryManagementStruct.pZoneArray->pageUsingCount,
//                memoryManagementStruct.pZoneArray->pageFreeCount);
//
//    printfColor(ORANGE, BLACK,
//                "start_code:%lx,end_code:%lx,end_data: %lx, end_brk: %lx, end_of_struct: %lx\n",
//                memoryManagementStruct.kernelStartCode, memoryManagementStruct.kernelEndCode,
//                memoryManagementStruct.kernelEndData, memoryManagementStruct.kernelEndBrk,
//                memoryManagementStruct.endOfStruct);


    return 1;
}

SlabCache *slabCreate(unsigned long size, void *(*constructor)(void *pVirtualAddress, unsigned long arg),
                      void *(*destructor)(void *pVirtualAddress, unsigned long arg), unsigned long arg) {

    // 用来记录分配好的 SlabCache 指针，用于返回
    SlabCache *pSlabCache = NULL;

    // 1. 为 SlabCache 结构体分配内存，也就是堆里面的内存
    pSlabCache = (SlabCache *) kmalloc(sizeof(SlabCache), 0);

    // 2. 判断是否分配成功，失败就返回 NULL
    if (pSlabCache == NULL) {

        printfColor(RED, BLACK, "slabCreate Faild!\n");
        return NULL;

    }

    // 3. 将分配的内存清空
    memset(pSlabCache, 0, sizeof(SlabCache));

    // 4. 设置结构体的变量
    pSlabCache->size = SIZEOF_LONG_ALIGN(size);
    pSlabCache->totalUsing = 0;
    pSlabCache->totalFree = 0;
    pSlabCache->pCacheDmaPool = NULL;
    pSlabCache->constructor = constructor;
    pSlabCache->destructor = destructor;
    pSlabCache->pCachePool = (Slab *) kmalloc(sizeof(Slab), 0);

    // 5. 判断分配 Slab 结构体的内存是否成功
    if (pSlabCache->pCachePool == NULL) {

        printfColor(RED, BLACK, "Slab kmalloc Faild!\n");

        // 一定要释放掉，因为这个不使用了
        kfree(pSlabCache);

        return NULL;

    }

    // 6. 清空分配的 Slab 的内存
    memset(pSlabCache->pCachePool, 0, sizeof(Slab));

    // 7. 将 Slab 里面的链表初始化一下
    listInit(&pSlabCache->pCachePool->list);

    // 8. 为分配器的物理页成员变量分配物理页，1页
    pSlabCache->pCachePool->pPage = allocPages(ZONE_NORMAL, 1, 0);

    // 9. 判断物理页是否分配成功
    if (pSlabCache->pCachePool->pPage == NULL) {

        printfColor(RED, BLACK, "pPage allocPages Faild!\n");

        kfree(pSlabCache->pCachePool);
        kfree(pSlabCache);

        return NULL;

    }

    // 10. 初始化页表，标明是给内核使用的
    pageInit(pSlabCache->pCachePool->pPage, PG_Kernel);

    // 11. 初始化 Slab 结构体
    pSlabCache->pCachePool->usingCount = PAGE_2M_SIZE / pSlabCache->size;
    pSlabCache->pCachePool->freeCount = pSlabCache->pCachePool->usingCount;
    pSlabCache->totalFree = pSlabCache->pCachePool->freeCount;
    pSlabCache->pCachePool->pVirtualAddress = Physical_To_Virtual(pSlabCache->pCachePool->pPage->physicalAddress);
    pSlabCache->pCachePool->colorCount = pSlabCache->pCachePool->freeCount;
    pSlabCache->pCachePool->colorLength =
            ((pSlabCache->pCachePool->colorCount + sizeof(unsigned long) * 8 - 1) >> 6) << 3;
    pSlabCache->pCachePool->pColorMap = (unsigned long *) kmalloc(pSlabCache->pCachePool->colorLength, 0);

    // 12. 判断给 pColorMap 分配内存是否成功
    if (pSlabCache->pCachePool->pColorMap == NULL) {

        printfColor(RED, BLACK, "pSlabCache->pCachePool->pColorMap kmalloc Faild!\n");

        freePages(pSlabCache->pCachePool->pPage, 1);
        kfree(pSlabCache->pCachePool);
        kfree(pSlabCache);

        return NULL;

    }

    // 13. 清空给 pColorMap 分配的内存
    memset(pSlabCache->pCachePool->pColorMap, 0, pSlabCache->pCachePool->colorLength);

    return pSlabCache;
}

unsigned long slabDestroy(SlabCache *pSlabCache) {

    Slab *pSlab = pSlabCache->pCachePool;
    Slab *pSlabTmp = NULL;

    // 1. 判断是否还有在使用的分配器
    if (pSlabCache->totalUsing != 0) {

        printfColor(RED, BLACK, "totalUsing != 0\n");
        return 0;
    }

    // 2. 判断当前 pSlab 的 list 成员是否为空
    while (!listIsEmpty(&pSlab->list)) {

        // 1> 保存一下当前的 pSlab
        pSlabTmp = pSlab;

        // 2> 获取到下一个 pSlab，为下一次循环做准备
        pSlab = containerOf(listNext(&pSlab->list), Slab, list);

        // 3> 删除刚才保存的 pSlabTmp
        listDel(&pSlab->list);

        // 4> 释放掉 pSlabTmp->pColorMap 占据的内存
        kfree(pSlabTmp->pColorMap);

        // 5> 清理页面
        pageClean(pSlabTmp->pPage);

        // 6> 释放掉页面
        freePages(pSlabTmp->pPage, 1);

        // 7> 释放掉 pSlabTmp 占据的内存
        kfree(pSlabTmp);

    }

    // 3. 因为当前的 pSlab 没有进入到步骤2 的循环，所以这里需要再做一次上面的动作
    kfree(pSlab->pColorMap);
    pageClean(pSlab->pPage);
    freePages(pSlab->pPage, 1);
    kfree(pSlab);

    // 4. 释放掉 pSlabCache
    kfree(pSlabCache);

    return 1;
}

void *slabMalloc(SlabCache *pSlabCache, unsigned long arg) {

    Slab *pSlab = pSlabCache->pCachePool;
    Slab *pSlabTmp = NULL;
    int j = 0;

    // 1. 判断高速缓存的 totalFree 是否为 0，如果为 0 就说明需要对分配器进行扩容了
    if (pSlabCache->totalFree == 0) {

        // 1> 创建一个 Slab 结构体
        pSlabTmp = (Slab *) kmalloc(sizeof(Slab), 0);

        // 2> 判断是否创建成功
        if (pSlabTmp == NULL) {

            printfColor(RED, BLACK, "pSlabTmp kmalloc Fiald!\n");

            return NULL;

        }

        // 3> 清空创建的结构体的内存空间
        memset(pSlabTmp, 0, sizeof(Slab));

        // 4> 初始化新创建的分配器的 list 成员
        listInit(&pSlabTmp->list);

        // 5> 为创建的分配器的物理页对象创建物理页
        pSlabTmp->pPage = allocPages(ZONE_NORMAL, 1, 0);

        // 6> 判断分配物理页是否成功
        if (pSlabTmp->pPage == NULL) {

            printfColor(RED, BLACK, "pSlabTmp->pPage allocPages Fiald\n");

            kfree(pSlabTmp);

            return NULL;
        }

        // 7> 初始化分配的物理页
        pageInit(pSlabTmp->pPage, PG_Kernel);

        // 8> 设置 pSlabTmp 的成员变量
        pSlabTmp->usingCount = PAGE_2M_SIZE / pSlabCache->size;
        pSlabTmp->freeCount = pSlabTmp->usingCount;
        pSlabTmp->pVirtualAddress = Physical_To_Virtual(pSlabTmp->pPage->physicalAddress);
        pSlabTmp->colorCount = pSlabTmp->freeCount;
        pSlabTmp->colorLength =
                ((pSlabTmp->colorCount + sizeof(unsigned long) * 8 - 1) >> 6) << 3;
        pSlabTmp->pColorMap = (unsigned long *) kmalloc(pSlabTmp->colorLength, 0);

        // 9> 判断给 pColorMap 分配内存是否成功
        if (pSlabTmp->pColorMap == NULL) {

            printfColor(RED, BLACK, "pSlabTmp>pColorMap kmalloc Faild!\n");

            freePages(pSlabTmp->pPage, 1);
            kfree(pSlabTmp);

            return NULL;

        }

        // 10> 将 pColorMap 清空
        memset(pSlabTmp->pColorMap, 0, pSlabTmp->colorLength);

        // 11> 把新创建的分配器插到 pSlabCache->pCachePool 的后面
        listAddToBehind(&pSlabCache->pCachePool->list, &pSlabTmp->list);

        // 12> 增加分配器缓冲区的总释放数的值
        pSlabCache->totalFree += pSlabTmp->colorCount;

        // 13> 遍历着色区，找出第一个可以使用的空闲内存对象
        for (j = 0; j < pSlabTmp->colorCount; j++) {

            // 判断是否为可以使用的
            if ((*(pSlabTmp->pColorMap + (j >> 6)) & (1UL << (j % 64))) == 0) {

                // A. 将当前选中的内存对象进行着色
                *(pSlabTmp->pColorMap + (j >> 6)) |= 1UL << (j % 64);

                // B. 改变当前分配器的使用记录属性
                pSlabTmp->usingCount++;
                pSlabTmp->freeCount--;

                // C. 更新分配器缓冲区使用记录属性
                pSlabCache->totalUsing++;
                pSlabCache->totalFree--;

                // D. 判断当前分配器是否有构造函数
                if (pSlabCache->constructor != NULL) {
                    return pSlabCache->constructor((char *) pSlabTmp->pVirtualAddress + pSlabCache->size * j, arg);
                } else {
                    return (void *) ((char *) pSlabTmp->pVirtualAddress + pSlabCache->size * j);
                }

            }

        }

    } else {

        do {

            if (pSlab->freeCount == 0) {

                pSlab = containerOf(listNext(&pSlab->list), Slab, list);
                continue;

            }

            for (j = 0; j < pSlab->colorCount; j++) {

                // 1> 如果一个区域是 0xffffffffffffffffUL ，那么就说明这个区域没有可用内存
                if (*(pSlab->pColorMap + (j >> 6)) == 0xffffffffffffffffUL) {

                    j += 63;
                    continue;

                }

                // 2> 判断是否为可以使用的
                if ((*(pSlab->pColorMap + (j >> 6)) & (1UL << (j % 64))) == 0) {

                    // A. 将当前选中的内存对象进行着色
                    *(pSlab->pColorMap + (j >> 6)) |= 1UL << (j % 64);

                    // B. 改变当前分配器的使用记录属性
                    pSlab->usingCount++;
                    pSlab->freeCount--;

                    // C. 更新分配器缓冲区使用记录属性
                    pSlabCache->totalUsing++;
                    pSlabCache->totalFree--;

                    // D. 判断当前分配器是否有构造函数
                    if (pSlabCache->constructor != NULL) {
                        return pSlabCache->constructor((char *) pSlab->pVirtualAddress + pSlabCache->size * j, arg);
                    } else {
                        return (void *) ((char *) pSlab->pVirtualAddress + pSlabCache->size * j);
                    }


                }

            }

        } while (pSlab != pSlabCache->pCachePool);

    }

    printfColor(RED, BLACK, "slabMalloc Fiald\n");

    // 如果创建Slab失败，就会到这里来，将所占据的资源释放掉
    if (pSlabTmp != NULL) {

        listDel(&pSlabTmp->list);
        kfree(pSlabTmp->pColorMap);
        pageClean(pSlabTmp->pPage);
        freePages(pSlabTmp->pPage, 1);
        kfree(pSlabTmp);

    }

    return NULL;
}

unsigned long slabFree(SlabCache *pSlabCache, void *address, unsigned long arg) {

    Slab *pSlab = pSlabCache->pCachePool;
    int index = 0;

    do {

        // 判断要释放的地址是否在这个分配器中
        if (pSlab->pVirtualAddress <= address && address < pSlab->pVirtualAddress + PAGE_2M_SIZE) {

            // A. 获取到当前这个地址的索引
            index = (int) ((address - pSlab->pVirtualAddress) / pSlabCache->size);

            // B. 把对应的着色区域去掉
            *(pSlab->pColorMap + (index >> 6)) ^= 1UL << index % 64;

            // C. 改变当前分配器的使用记录属性
            pSlab->usingCount++;
            pSlab->freeCount--;

            // D. 更新分配器缓冲区使用记录属性
            pSlabCache->totalUsing++;
            pSlabCache->totalFree--;

            // E. 判断析构函数是否存在
            if (pSlabCache->destructor != NULL) {
                pSlabCache->destructor((char *) pSlab->pVirtualAddress + pSlabCache->size * index, arg);
            }

            // F. 如果这个分配器使用的此时为0，并且总共的释放数大于此分配器的着色数量的1.5倍，那么就释放这个分配器，这样做是为了降低系统内存使用率
            if (((pSlab->usingCount == 0) && (pSlabCache->totalFree >= pSlab->colorCount * 3 / 2))) {

                listDel(&pSlab->list);

                pSlabCache->totalFree -= pSlab->colorCount;

                kfree(pSlab->pColorMap);

                pageClean(pSlab->pPage);
                freePages(pSlab->pPage, 1);
                kfree(pSlab);

            }

            return 1;

        } else {

            // 换到下一个分配器
            pSlab = containerOf(listNext(&pSlab->list), Slab, list);
            continue;

        }

    } while (pSlab != pSlabCache->pCachePool);

    printfColor(RED, BLACK, "slabFree Fiald!\n");

    return 0;
}